package com.tuozixuan.code;

import java.io.File;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.yaml.snakeyaml.Yaml;

import com.tuozixuan.code.config.CodeProperties;
import com.tuozixuan.code.config.CodeWrapProperties;
import com.tuozixuan.code.core.CodeGenerator;
import com.tuozixuan.code.core.TableInfoHelper;
import com.tuozixuan.code.jdbc.JdbcContext;
import com.tuozixuan.code.util.StringHolder;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.json.JSONUtil;

@Mojo(name = "generate")
public class CodeGeneratorMojo extends AbstractMojo {
	
	@Parameter(defaultValue = "${project.basedir}", readonly = true)
	private File basedir;
	
	@Parameter(defaultValue = "${project.build.directory}", readonly = true)
	private File target;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		
		CodeProperties codeProperties = this.initConfig();
		
		JdbcContext jdbcContext = new JdbcContext(codeProperties.getDatasource());
		TableInfoHelper tableInfoHelper = new TableInfoHelper(jdbcContext, this.getLog());
		CodeGenerator codeGenerator = new CodeGenerator(codeProperties, tableInfoHelper);
		codeGenerator.generate();
	}
	
	private CodeProperties initConfig() {
		
		String defaultConfigContent = ResourceUtil.readUtf8Str("classpath:generator.yml");
		Yaml yaml = new Yaml();
		Map<String, Object> defaultConfigMap = (Map<String, Object>) yaml.load(defaultConfigContent);
		Map<String, Object> defaultCodeConfigMap = (Map<String, Object>) defaultConfigMap.get("code");
		
    	
		String configFilePath = this.getConfigFilePath();
    	String configContent = ResourceUtil.readUtf8Str(configFilePath);
    	
    	Map<String, Object> configMap = (Map<String, Object>) yaml.load(configContent);
    	this.getLog().debug("configMap:" + configMap);
    	
    	defaultCodeConfigMap.putAll((Map<String, Object>)configMap.get("code"));
    	this.getLog().debug("final configMap:" + configMap);
    	
    	String configJsonStr = JSONUtil.toJsonStr(defaultConfigMap);
    	this.getLog().debug(" :" + configJsonStr);
    	
    	StringHolder configJsonStrHolder = StringHolder.of(configJsonStr);
    	
    	String[] dynamicKeys = StringUtils.substringsBetween(configJsonStr, "${", "}");
    	Stream.of(dynamicKeys).distinct().forEach(dynamicKey -> {
    		String dynamicValue = this.getDynamicValue(configMap, dynamicKey);
    		this.getLog().debug("dynamicKey:" + dynamicKey + " dynamicValue:" + dynamicValue);
    		String configJsonStrLocal = StringUtils.replace(configJsonStrHolder.getValue(), "${" + dynamicKey + "}", dynamicValue.replace("\\", "\\\\"));
    		configJsonStrHolder.setValue(configJsonStrLocal);
    	});
    	
    	configJsonStr = configJsonStrHolder.getValue();
    	this.getLog().debug("configJsonStr2:" + configJsonStr);
    	
    	CodeWrapProperties codeWrapProperties = JSONUtil.toBean(configJsonStr,  CodeWrapProperties.class);
    	this.getLog().debug("codeProperties:" + JSONUtil.toJsonStr(codeWrapProperties));
    	return codeWrapProperties.getCode();
    }
    
    private String getDynamicValue(Map<String, Object> paramMap, String dynamicKey) {
        
        Object value = null;
        Map<String, Object> localParamMap = paramMap;
        String[] dynamicKeyItems = dynamicKey.split("\\.");
        for (int i = 0; i < dynamicKeyItems.length; i++) {
            value = localParamMap.get(dynamicKeyItems[i]);
            if (i != dynamicKeyItems.length - 1) {
                localParamMap = (Map<String, Object>) value;
            }
        }
        
        Objects.requireNonNull(value, "動態值獲取不到 dynamicKey -> " + dynamicKey);
        return value.toString();
    }
    
    private String getConfigFilePath() {
    	
    	String configFileName = "generator.yml";
    	String targetPath = target.getAbsolutePath();
    	
    	String fullPathName = targetPath + File.separator + "classes" + File.separator + configFileName;
    	if (!FileUtil.exist(fullPathName)) {
    		fullPathName = targetPath + File.separator + "config" + File.separator + configFileName;
    	}
    	
    	this.getLog().debug("config path:" + fullPathName);
    	return fullPathName;
    }
    

}
